#include "logger.h"
#include <iostream>
namespace sylar{

/*
Function implement for LogEvent
*/
LogEvent::LogEvent(LogLevel::Level level, const char* file, int32_t line, int32_t threadId, int32_t fiberId, int64_t time, std::string& content) :
    m_level(level), m_file(file), m_line(line), m_threadId(threadId), m_fiberId(fiberId), m_time(time), m_content(content){
};


/*
Function implement for Logger
*/
Logger::Logger(const std::string& name) : m_name(name), m_level(LogLevel::INFO){
};
void Logger::log(LogLevel::Level level, LogEvent::smart_ptr event){
    if (level >= m_level){
        event->set_loggerName(m_name);
        event->set_level(level);
        for (auto & app : m_appenders){
            app->log(level, event);
        }
    }
};
    
void Logger::debug(LogEvent::smart_ptr event){
    log(LogLevel::DEBUG, event);
};
void Logger::info(LogEvent::smart_ptr event){
    log(LogLevel::INFO, event);
};
void Logger::warn(LogEvent::smart_ptr event){
    log(LogLevel::WARN, event);

};
void Logger::error(LogEvent::smart_ptr event){
    log(LogLevel::ERROR, event);

};
void Logger::fatal(LogEvent::smart_ptr event){
    log(LogLevel::FATAL, event);

};
void Logger::addAppender(LogAppender::smart_ptr appender){
    m_appenders.push_back(appender);
}
void Logger::deleteAppender(LogAppender::smart_ptr appender){
    for (auto it = m_appenders.begin(); it!=m_appenders.end(); it++){
        if (*it == appender){
            m_appenders.erase(it);
            break;
        }
    }
}


/*
Function implement for LogAppender
*/
LogAppender::LogAppender(LogFormatter::smart_ptr formatter) : m_formatter(formatter), m_level(LogLevel::INFO){

};

/*
Function implement for FileAppender
*/
FileLogAppender::FileLogAppender(const std::string& filename) : m_filename(filename){
        m_filestream.open(m_filename);
};
void FileLogAppender::log(LogLevel::Level level, LogEvent::smart_ptr event){
    if (level >= m_level){
        m_filestream << m_formatter->format(event);
    }
};
bool FileLogAppender::reopen(){
    if (m_filestream)
        m_filestream.close();
    m_filestream.open(m_filename);
    return !!m_filestream;
};


/*
Function implement for StdoutAppender
*/
void StdoutLogAppender::log(LogLevel::Level level, LogEvent::smart_ptr event){
    if (level >= m_level)
        std::cout << m_formatter->format(event);
};

// -------------------------- Log Items ----------------------------
/*
%m for message content
%p for level
%r for time after program start
%c for logger name
%t for thread id
%n for \n
%f for filename
%l for line
*/
class MessageItem : public FormatItem{
public:
    ~MessageItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << *event->get_content();
    }
};

class LevelItem : public FormatItem{
public:
    ~LevelItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << LogLevel::ToString(event->get_level());
    }
};

class TimeItem : public FormatItem{
public:
    ~TimeItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << event->get_time();
    }    
};

class LoggerNameItem : public FormatItem{
public:
    ~LoggerNameItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << event->get_logger();
    }    
};

class ThreadIdItem : public FormatItem{
public:
    ~ThreadIdItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << event->get_threadId();
    }    
};

class EnterItem : public FormatItem{
public:
    ~EnterItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << '\n';
    }    
};

class FilenameItem : public FormatItem{
public:
    ~FilenameItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << event->get_filename();
    }    
};

class LineNumItem : public FormatItem{
public:
    ~LineNumItem() = default;
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << event->get_line();
    }    
};

class TextItem : public FormatItem{
public:
    ~TextItem() = default;
    TextItem(const std::string& text) : m_text(text){}
    void format(std::stringstream& ss, LogEvent::smart_ptr event) override{
        ss << m_text;
    }
    std::string get_text() const{
        return m_text;
    }
private:
    std::string m_text;
};


/*
Function implement for LogFormatter
*/

LogFormatter::LogFormatter(const std::string& format) : m_pattern(format){
    std::list<std::tuple<int, std::string, char>> list;
    std::string str;
    for (size_t i=0; i<m_pattern.length()-1; i++){    
        if (m_pattern[i] != '%')
            str.append(1, m_pattern[i]);
        else if (m_pattern[i+1] == '%') {
            str.append(1, m_pattern[i + 1]);
            i++;
        }
        else{
            i++;
            if (str.length() != 0)
                list.emplace_back(std::make_tuple(0, str, '\0')), str.clear();
            list.emplace_back(std::make_tuple(1, "",m_pattern[i]));
        }
    }
    if (m_pattern[m_pattern.length()-1] == '%'){
        std::cerr << "Error parsing log pattern" << m_pattern << ": Illegal % at the end. Ignore" << std::endl;
    }
    for (auto& i : list){
        if (std::get<0>(i) == 0){
            auto ptr = FormatItem::smart_ptr(new TextItem(std::get<1>(i)));
            m_items.emplace_back(ptr);
        }
        else{
            auto item = FormatItem::smart_ptr();
            switch (std::get<2>(i))
            {
                case 'm': item.reset(new MessageItem()); m_items.push_back(item); break;
                case 'p': item.reset(new LevelItem()); m_items.push_back(item); break;
                case 'r': item.reset(new TimeItem()); m_items.push_back(item); break;
                case 'c': item.reset(new LoggerNameItem()); m_items.push_back(item); break;
                case 't': item.reset(new ThreadIdItem()); m_items.push_back(item); break;
                case 'n': item.reset(new EnterItem()); m_items.push_back(item); break;
                case 'f': item.reset(new FilenameItem()); m_items.push_back(item); break;
                case 'l': item.reset(new LineNumItem()); m_items.push_back(item); break;
            default:
                std::cout << "Invalid: %" << std::get<2>(i) << " in format string: " << m_pattern << std::endl;
                break;
            }
        }
    }
    m_items.emplace_back(FormatItem::smart_ptr(new TextItem("\n")));
}
/*
%m for message content
%p for level
%r for time after program start
%c for logger name
%t for thread id
%n for \n
%f for filename
%l for line
*/

std::string LogFormatter::format(LogEvent::smart_ptr event){
    std::stringstream ss;
    for (auto i : m_items){
        i->format(ss, event);
    }
    return ss.str();
};

}
